#include <utility> 
#include <iostream>
#include <memory>

#ifndef TEMPLATE_INVOKE_H_
#define TEMPLATE_INVOKE_H_
// min version gcc = c++ 17 
 //


template<class T >
class fun_obj {
public:
    fun_obj(T &container):container_(container){
    }
    void operator()(){
    }
    template < class N, class ...Args>
    void operator()(N head, Args... args) {
        container_.push_back(head);
        operator()( args...);
    }
private:
    T &container_;
};
int main() {

    std::vector<int> vec{1,3,4,5,8};
    fun_obj<std::vector<int>> obj(vec);
   // fun_obj obj(vec);   c++17 is ok
    obj(9,9,9,9,9,9);
}









namespace TInvoke
{


	// test class
	class BaseClassForFunCall
	{
	public:
		int BaseAddTwoNum(int a, int b)
		{
			std::cout << "baseAddTwoNum" << std::endl;
			return a + b;
		}
	};

	class ClassForFunCall :public BaseClassForFunCall
	{
	public:
		int addtwonum(int a, int b)
		{
			std::cout << "addtwonum" << std::endl;
			return a + b;
		}

		int addtwonumconst(int a, int b) const
		{
			std::cout << "const addtwonum" << std::endl;
			return a + b;
		}

		static float addtwofloatnum(float a, float b)
		{
			std::cout << "static addtwofloatnum" << std::endl;
			return a + b;
		}
	};

	// member function template
	template<typename R, typename T, typename... ARGS>
	struct AnyMemberFunCall
	{
		using FunType = R(T::*)(ARGS...);
		AnyMemberFunCall(T* pkObj, FunType pfFun) :m_pkObj(pkObj), m_pfFun(pfFun) {};
		R DoInvoke(ARGS&&... args) { return (m_pkObj->*m_pfFun)(std::forward<ARGS>(args)...); };
	private:
		T* m_pkObj = nullptr;
		FunType m_pfFun = nullptr;
	};

	template<typename R, typename T, typename... ARGS>
	auto CreateAnyMemberFunCall(T* pkObj, R(T::* pkFun)(ARGS...))   // R(T::* pkFun) 这个根本看不懂  只要记住是这个声明就好了
		//	->decltype(AnyMemberFunCall(pkObj, pkFun)) R()
	{
		return AnyMemberFunCall(pkObj, pkFun);
	}

};


static void Template_Test_Invoke()
{
	using namespace TInvoke;

	// test for common member fuction
	ClassForFunCall c1;
	auto caller = CreateAnyMemberFunCall(&c1, &ClassForFunCall::addtwonum);
	caller.DoInvoke(12, 15);

	
}


int main() {

	Template_Test_Invoke();
}



















namespace TInvoke
{
	// member function template
	template<typename R,typename T,typename... ARGS>
	struct AnyMemberFunCall
	{
		using FunType = R(T::*)(ARGS...);
		AnyMemberFunCall(T* pkObj,FunType pfFun):m_pkObj(pkObj),m_pfFun(pfFun) {};
		R DoInvoke(ARGS&&... args){return (m_pkObj->*m_pfFun)(std::forward<ARGS>(args)...);};
	private:
		T* m_pkObj = nullptr;
		FunType m_pfFun = nullptr;
	};

	template<typename R,typename T,typename... ARGS>
	auto CreateAnyMemberFunCall(T* pkObj,R(T::*pkFun)(ARGS...))
			->decltype(AnyMemberFunCall(pkObj,pkFun))
	{
		return AnyMemberFunCall(pkObj,pkFun);
	}

	// use the enable for detech
	template<bool bCheck,typename T = void>
	struct MyEnableIf{};

	template<typename T>
	struct MyEnableIf<true,T>
	{
		using Type = T;
	};

	template<typename B,typename D>
	struct IsDClassFromBClass
	{
		using B_D_T = std::decay_t<B>;
		using D_D_T = std::decay_t<D>;
		static auto Check(B_D_T*)->int;
		static auto Check(...)->void;
		static constexpr bool Value = !std::is_void_v<decltype(Check((D_D_T*)0))>;
	};

	template<typename B,typename D,typename MyEnableIf<IsDClassFromBClass<B,D>::Value,std::decay_t<D>>::Type* = nullptr>
	auto GetRightMemberType(D&& obj)->decltype(static_cast<D&&>(obj))
	{
		return static_cast<D&&>(obj);
	}

	template<typename B,typename D,typename MyEnableIf<!IsDClassFromBClass<B,D>::Value,std::decay_t<D>>::Type* = nullptr>
	auto GetRightMemberType(D&& obj)->decltype(*(static_cast<D&&>(obj)))
	{
		return *(static_cast<D&&>(obj));
	}

	template<typename F,typename... ARGS>
	auto CommonInvokeCall(F&& funObj,ARGS&&... args)
			->decltype(std::forward<F>(funObj)(std::forward<ARGS>(args)...))
	{
		return std::forward<F>(funObj)(std::forward<ARGS>(args)...);
	}


	//member function
	template<typename R,typename T,typename C,typename... ARGS>
	auto CommonInvokeCall(R (T::*pfMem)(ARGS...),C&& obj,ARGS&&... args)
			->decltype((GetRightMemberType<T>(std::forward<C>(obj)).*pfMem)(std::forward<ARGS>(args)...))
	{

		return (GetRightMemberType<T>(std::forward<C>(obj)).*pfMem)(std::forward<ARGS>(args)...);
	}

	template<typename R,typename T,typename C,typename... ARGS>
	auto CommonInvokeCall(R (T::*pfMem)(ARGS...) const,C&& obj,ARGS&&... args)
			->decltype((GetRightMemberType<T>(std::forward<C>(obj)).*pfMem)(std::forward<ARGS>(args)...))
	{

		return (GetRightMemberType<T>(std::forward<C>(obj)).*pfMem)(std::forward<ARGS>(args)...);
	}

	// test class
	class BaseClassForFunCall
	{
	public:
		int BaseAddTwoNum(int a,int b) 
		{
			std::cout << "baseAddTwoNum" << std::endl;
			return a+b;
		}
	};

	class ClassForFunCall:public BaseClassForFunCall
	{
	public:
		int addtwonum(int a,int b) 
		{
			std::cout << "addtwonum" << std::endl;
			return a+b;
		}

		int addtwonumconst(int a,int b) const
		{
			std::cout << "const addtwonum" << std::endl;
			return a+b;
		}

		static float addtwofloatnum(float a,float b)
		{
			std::cout << "static addtwofloatnum" << std::endl;
			return a+b;	
		}
	};


	struct myfunctor
	{
		std::string operator()(const std::string& a,const std::string& b)
		{
			std::cout << "a string:" << a.c_str() << std::endl;
			std::cout << "b string" << b.c_str() << std::endl;
			return a+b;
		}
	};

	std::string testfun1(const std::string& a,const std::string& b)
	{
		std::cout << "a string:" << a.c_str() << std::endl;
		std::cout << "b string" << b.c_str() << std::endl;
		return a+b;
	}


	//just for the self class member 
	template<typename R,typename T,typename... ARGS>
	auto CommonInvokeCallOne(R (T::*pfMem)(ARGS...),T obj,ARGS&&... args)
			->decltype((obj.*pfMem)(std::forward<ARGS>(args)...))
	{
		return (obj.*pfMem)(std::forward<ARGS>(args)...);
	}

	template<typename R,typename T,typename... ARGS>
	auto CommonInvokeCallOne(R (T::*pfMem)(ARGS...) const,T obj,ARGS&&... args)
			->decltype((obj.*pfMem)(std::forward<ARGS>(args)...))
	{
		return (obj.*pfMem)(std::forward<ARGS>(args)...);
	}

	//can for base class member funciton and move 
	template<typename R,typename T,typename C,typename... ARGS>
	auto CommonInvokeCallTwo(R (T::*pfMem)(ARGS...),C&& obj,ARGS&&... args)
			->decltype((std::forward<C>(obj).*pfMem)(std::forward<ARGS>(args)...))
	{

		return (std::forward<C>(obj).*pfMem)(std::forward<ARGS>(args)...);
	}

	template<typename R,typename T,typename C,typename... ARGS>
	auto CommonInvokeCallTwo(R (T::*pfMem)(ARGS...) const,C&& obj,ARGS&&... args)
			->decltype((std::forward<C>(obj).*pfMem)(std::forward<ARGS>(args)...))
	{

		return (std::forward<C>(obj).*pfMem)(std::forward<ARGS>(args)...);
	}
};


static void Template_Test_Invoke()
{
	using namespace TInvoke;

	// test for common member fuction
	ClassForFunCall c1;
	auto caller = CreateAnyMemberFunCall(&c1,&ClassForFunCall::addtwonum);
	caller.DoInvoke(12,15);

	// // test for common base member function
	//auto caller1 = CreateAnyMemberFunCall(&c1,&ClassForFunCall::BaseAddTwoNum); //error,can't detech
	//std::cout << caller1.DoInvoke(100,200) << std::endl;


	// test for functor
	myfunctor funobj;
	const std::string a1 = "hello ",b1 = "c++ world1";
	auto aret1 = CommonInvokeCall(funobj,"hello ","c++ world1");
	std::cout << aret1.c_str() << std::endl;
	auto aret11 = CommonInvokeCall(funobj,a1,b1);
	std::cout << aret11.c_str() << std::endl;

	//test for lambda
//	CommonInvokeCall([](const std::string& a,const std::string& b)->decltype(a+b)\
		{std::cout << "lambda " << a1.c_str() << b1.c_str() << std::endl;return a+b;},a1,b1);	

	// test for common function
	testfun1("hello ","c++ world2");
	const std::string a = "hello ",b = "c++ world3";
	auto aret2 = CommonInvokeCall(testfun1,a,b);
	std::cout << aret2.c_str() << std::endl;


	// test for static member function
	auto aret3 = CommonInvokeCall(ClassForFunCall::addtwofloatnum,22.42f,100.2f);
	std::cout << aret3 << std::endl;

	// test for common member function
	auto aret4 = CommonInvokeCall(&ClassForFunCall::addtwonum,c1,100,200);
	std::cout << aret4 << std::endl;

	// test for common member function
	auto aret5 = CommonInvokeCall(&ClassForFunCall::addtwonumconst,c1,100,300);
	std::cout << aret5 << std::endl;


	// // test for common base member function
	auto aret6 = CommonInvokeCall(&ClassForFunCall::BaseAddTwoNum,c1,100,400);
	std::cout << aret6 << std::endl;


	ClassForFunCall* pkC1 = &c1;
	auto aret7 = CommonInvokeCall(&ClassForFunCall::BaseAddTwoNum,pkC1,100,500);
	std::cout << aret7 << std::endl;

	// test for not compatible class
	//BaseClassForFunCall bc1;
	//auto aret8 = CommonInvokeCall(&ClassForFunCall::addtwonumconst,bc1,100,200);
	// std::cout << aret8 << std::endl;
}


int main(){

    Template_Test_Invoke();
}

















#endif